import { Injectable, OnModuleInit } from '@nestjs/common';
import { createHash, randomUUID } from 'node:crypto'
import * as mqtt from "mqtt";
import { DeviceRepositoryService } from '@app/db/device-repository/device-repository.service';
import { DeviceSecretRepositoryService } from '@app/db/device-secret-repository/device-secret-repository.service';
import { HttpService } from '@nestjs/axios';
import { In } from 'typeorm';
import { CacheService } from '@app/cache';
import * as dayjs from 'dayjs';
import { ConfigureService } from '@app/configure';

@Injectable()
export class AppService implements OnModuleInit{
    private baseUrl = 'https://iot.nm3.sg';
    private token = '';
	constructor(
        private deviceRepository:DeviceRepositoryService,
        private deviceSecretRepository:DeviceSecretRepositoryService,
        private readonly httpService: HttpService,
        private readonly cache:CacheService,
        private readonly config:ConfigureService
    ){}

	md5(str:string) {
		return createHash('md5').update(str).digest('hex').toLowerCase()
	}
	
    private async login() {
        const params = {"isDemoAccount":false,"userName":"sogo","password":"25d55ad283aa400af464c76d713c07ad","loginType":"userName","systemVersion":"Windows 10","systemType":"Windows","browserType":"chrome","browserVersion":"128.0.0.0","ticket":null,"companyId":"1","domainName":"iot.nm3.sg"}

		try {
            const res = await this.httpService.axiosRef.post(this.baseUrl + '/iot/token/get', params)

		
		
            if(!(res.data && res.data.data && res.data.data.accessToken)) {
                console.log('登录失败')
                console.log(res)
            }


            this.token = res.data.data.accessToken
        } catch (error) {
            
        }
        setTimeout(() => {
            this.login()
        }, 60 * 30 * 1000)
        
    }

    public async getRecord(key:string) {
        const res = await this.cache.get(key)
        return res ? JSON.parse(res) : null
    }

    public async setRecord(key:string, data:any[]) {
        this.cache.set(key, JSON.stringify(data))
    }


	public async onModuleInit() {
        console.log('-------------启动2----------')

        await this.login()


		const username = 'fsfvywgvsg5v543346456sfrgbstsw'
		const p0 = username.slice(0, 4)
		const p1 = username.slice(4, username.length - 4)
		const p2 = username.slice(username.length - 4)

		const password = createHash('sha256').update(this.md5(p0) + this.md5(p1) + this.md5(p2)).digest('hex').toLowerCase()


		const clientId = randomUUID()
		

		const client = mqtt.connect(this.config.get('MQTT_HOST'), {
			clientId: clientId,
			username : username,
			password : password,
		})

		
		client.on("connect", function () {
			console.log('链接成功')
			client.subscribe('/#', (err) => {
				console.log(err)
				console.log('订阅成功')
			});
		});

		client.on('message', async (topic, message) => {

			let msg = null

            try {
                msg = JSON.parse(message.toString())
            } catch (error) {
                // console.log(message.toString())
                // console.log(message)
                // console.log(error)

                return
            }

            if(msg == null) return

            const time = dayjs().unix()

            const deviceCore = msg.dCore

            


			if(deviceCore == undefined) return

			if(!msg.sData || msg.sData.length == 0) return


			msg.sData.forEach(async item => {
                const vals = item.val
                if(!vals) return


                

                if(vals.eRainfall != undefined) {
                    console.log((new Date()).toLocaleTimeString())
                    console.log(message.toString())


                    try {
                        const value = vals.eRainfall ///上报的水量
                        


                        const device = await this.deviceRepository.findOne({deviceCore : deviceCore, deleteStatus : false}, ['deviceId', 'rainFallSetting'])
                        if(!device.rainFallSetting || !device.rainFallSetting.devices) return


                        const dryKey = 'dryFall:' + device.deviceId
                        const cacheKey = 'rainFall:' + device.deviceId

                        


                        const dryData = await this.getRecord(dryKey)
                        if(dryData) {
                            console.log(dryData)
                            const timeOffset = Math.floor(time - dryData[1])
                            if(timeOffset > device.rainFallSetting.rainy.time * 60) {
                                this.cache.del(dryKey)
                                this.setRecord(cacheKey, [[value, time]])
                                return
                            }



                            if(Math.abs(value - dryData[0]) <= (device.rainFallSetting.dry.offset || 0)) { //没有变化的
                                if(timeOffset >= (device.rainFallSetting.dry.time * 60)) {
                                    this.cache.del(dryKey)
                                    return
                                }
                            }else {
                                this.setRecord(dryKey, [value, time])
                                return
                            }

                        }



                        const data = await this.getRecord(cacheKey) //这是服务器存储的
                        if(!data) {
                            this.setRecord(cacheKey, [[value, time]])
                            return
                        }

                        console.log(data)

                        const rainyTime = device.rainFallSetting.rainy.time * 60

                        let delLen = 0;

                        for(let i = 0; i < data.length; i ++) {
                            const timeOffset = Math.floor(time - data[i][1])
                            
                            if(timeOffset >= rainyTime) {
                                delLen += 1;
                            }

                            const valueOffset = value - data[i][0]

                            if(valueOffset >= device.rainFallSetting.rainy.offset && timeOffset <= rainyTime) { //触发采样
                                this.setRecord(dryKey, [value, time])
                                this.cache.del(cacheKey)


                                if(device.rainFallSetting.devices.length == 0) return

                                const devices = await this.deviceRepository.find({deviceCore : In(device.rainFallSetting.devices)}, {deviceId : true, deviceCore : true})
                                const deviceSecrets = await this.deviceSecretRepository.find({deviceId : In(devices.map(v => v.deviceId))}, {deviceKey : true, deviceId : true})

                                deviceSecrets.forEach(item => {
                                    this.httpService.axiosRef.post(this.baseUrl + '/iot/device/setting/send', {
                                        deviceKey : item.deviceKey,
                                        deviceId : item.deviceId,
                                        keyName : 'setStartSD',
                                        port : '0',
                                        value : '1'
                                    }, {headers : {'access-token' : this.token}}).then(r => {
                                    })
                                })

                                return

                            }
                            
                        }

                        if(delLen > 0) {
                            data.splice(0, delLen)
                        }

                        data.push([value, time])

                        this.setRecord(cacheKey, data)
                        
                    } catch (error) {
                        console.log(error.message)
                        
                    }


                    return
                }

				if(vals.eRainfall != undefined) {
                    console.log((new Date()).toLocaleTimeString())
                    console.log(message.toString())
                    try {


                        const value = vals.eRainfall ///上报的水量

                        

                        const device = await this.deviceRepository.findOne({deviceCore : deviceCore, deleteStatus : false}, ['deviceId', 'rainFallSetting'])
                        
                        console.log(device)

                        const cacheKey = 'rainFall:' + device.deviceId
                        const data = await this.cache.hgetall(cacheKey) //这是服务器存储的
                        console.log(data)

                        if(!data || Object.keys(data).length == 0) {
                            this.cache.hmset(cacheKey, {v : value, t : time, lt : time})
                            return
                        }

                    
                        const prevValue = Number(data.v) //上次的值
                        const prevTime = Number(data.t) //上次的时间
                        const timeOffset = time - prevTime
                        const valueOffset = value - prevValue

                        console.log('timeoff:' + timeOffset)
                        console.log('valueOffset:' + valueOffset)

                        if(timeOffset > device.rainFallSetting.rainy.time * 60) { //如果
                            this.cache.client.hdel(cacheKey, 'f')
                            this.cache.hmset(cacheKey, {v : value, t : time, lt : time})
                            return
                        }


                
                        // if(value == 0 || value < prevValue) { //上报的值如果等于0，或者小于服务器存储的，那就更新服务器存储的
                        //     this.cache.client.hdel(cacheKey, 'f')
                        //     this.cache.hmset(cacheKey, {v : value, t : time, lt : time})
                            
                        //     return
                        // }


                        

                        
                        

                        if(data.f != undefined) {
                            if(Math.abs(valueOffset) <= (device.rainFallSetting.dry.offset || 0)) { //没有变化的
                                if(timeOffset >= (device.rainFallSetting.dry.time * 60)) {
                                    this.cache.del(cacheKey)
                                    return
                                }
                            }else {
                                this.cache.hmset(cacheKey, {v : value, t : time, f : 1, lt : time})
                                return
                            }
                        }





                        let needSend = false

                        if(valueOffset > device.rainFallSetting.rainy.offset && timeOffset <= device.rainFallSetting.rainy.time * 60) needSend = true //30分钟内达到了10mm 1800，执行采样
                        

                        if(needSend) {

                            this.cache.hmset(cacheKey, {v : value, t : time, f : 1, lt : time})


                            if(!device.rainFallSetting || !device.rainFallSetting.devices || device.rainFallSetting.devices.length == 0) return

                            const devices = await this.deviceRepository.find({deviceCore : In(device.rainFallSetting.devices)}, {deviceId : true, deviceCore : true})
                            const deviceSecrets = await this.deviceSecretRepository.find({deviceId : In(devices.map(v => v.deviceId))}, {deviceKey : true, deviceId : true})

                            deviceSecrets.forEach(item => {
                                this.httpService.axiosRef.post(this.baseUrl + '/iot/device/setting/send', {
                                    deviceKey : item.deviceKey,
                                    deviceId : item.deviceId,
                                    keyName : 'setStartSD',
                                    port : '0',
                                    value : '1'
                                }, {headers : {'access-token' : this.token}}).then(r => {
                                })
                            })
                        }
                        
                    } catch (error) {
                        console.log(error.message)
                    }
				}
			})
			
		})


	}
}
